package com.itsmurphy.flutter_audio_output;

import androidx.annotation.NonNull;

import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.AudioDeviceInfo;
import android.content.Context;
import android.os.Build;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

/**
 * FlutterAudioOutputPlugin
 */
public class FlutterAudioOutputPlugin implements FlutterPlugin, MethodCallHandler {
    private static final String TAG = "FlutterAudioOutput";

    /// The MethodChannel that will the communication between Flutter and native Android
    ///
    /// This local reference serves to register the plugin with the Flutter Engine and unregister it
    /// when the Flutter Engine is detached from the Activity
    private MethodChannel channel;
    private AudioManager audioManager;
    private Context context;
    private AudioChangeReceiver audioChangeReceiver;

    @Override
    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
        channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "flutter_audio_output");
        channel.setMethodCallHandler(this);
        context = flutterPluginBinding.getApplicationContext();
        audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

        // Register audio change receiver
        audioChangeReceiver = new AudioChangeReceiver(listener);
        IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
        context.registerReceiver(audioChangeReceiver, filter);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            audioManager.registerAudioDeviceCallback(new android.media.AudioDeviceCallback() {
                @Override
                public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
                    // 音频设备已添加
                    listener.onChanged();
                }

                @Override
                public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
                    // 音频设备已移除
                    listener.onChanged();
                }
            }, null);
        }
    }

    private final AudioEventListener listener = new AudioEventListener() {
        @Override
        public void onChanged() {
            if (channel != null) {
                channel.invokeMethod("inputChanged", 1);
            }
        }
    };

    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
        if (call.method.equals("getPlatformVersion")) {
            result.success("Android " + android.os.Build.VERSION.RELEASE);
        } else if (call.method.equals("getCurrentOutput")) {
            result.success(getCurrentOutput());
        } else if (call.method.equals("getAvailableInputs")) {
            result.success(getAvailableInputs());
        } else if (call.method.equals("changeToReceiver")) {
            result.success(changeToReceiver());
        } else if (call.method.equals("changeToSpeaker")) {
            result.success(changeToSpeaker());
        } else if (call.method.equals("changeToHeadphones")) {
            result.success(changeToHeadphones());
        } else if (call.method.equals("changeToBluetooth")) {
            result.success(changeToBluetooth());
        } else {
            result.notImplemented();
        }
    }

    private Boolean changeToReceiver() {
        audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
        audioManager.stopBluetoothSco();
        audioManager.setBluetoothScoOn(false);
        audioManager.setSpeakerphoneOn(false);
        listener.onChanged();
        return true;
    }

    private Boolean changeToSpeaker() {
        audioManager.setMode(AudioManager.MODE_NORMAL);
        audioManager.stopBluetoothSco();
        audioManager.setBluetoothScoOn(false);
        audioManager.setSpeakerphoneOn(true);
        listener.onChanged();
        return true;
    }

    private Boolean changeToHeadphones() {
        return changeToReceiver();
    }

    private Boolean changeToBluetooth() {
        audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
        audioManager.startBluetoothSco();
        audioManager.setBluetoothScoOn(true);
        listener.onChanged();
        return true;
    }

    private List<String> getCurrentOutput() {
        List<String> info = new ArrayList<>();

        AudioDeviceInfo[] audioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
        info = getInfo(audioDevices[0]);

        return info;
    }

    private List<String> getInfo(AudioDeviceInfo deviceInfo) {
        String type = "0";
        switch (deviceInfo.getType()) {
            case AudioDeviceInfo.TYPE_BUILTIN_MIC:
                type = "1";
                break;
            case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
            case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER_SAFE:
                type = "2";
                break;
            case AudioDeviceInfo.TYPE_WIRED_HEADSET:
            case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
                type = "3";
                break;
            case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
            case AudioDeviceInfo.TYPE_BLUETOOTH_SCO:
            case AudioDeviceInfo.TYPE_BLE_HEADSET:
            case AudioDeviceInfo.TYPE_BLE_SPEAKER:
            case AudioDeviceInfo.TYPE_BLE_BROADCAST:
                type = "4";
                break;
            case AudioDeviceInfo.TYPE_USB_DEVICE:
            case AudioDeviceInfo.TYPE_USB_HEADSET:
            case AudioDeviceInfo.TYPE_USB_ACCESSORY:
                type = "5";
                break;
            default:
                type = "0";
                break;
        }
        String temp = Build.VERSION.SDK_INT < 28 ? "" : ", " + deviceInfo.getAddress();
        List<String> infos = Arrays.asList(String.valueOf(deviceInfo.getProductName()) + " (" + typeToString(deviceInfo.getType()) + temp + ")", String.valueOf(deviceInfo.getId()), type);
        return infos;
    }

    private List<List<String>> getAvailableInputs() {
        List<List<String>> list = new ArrayList<>();

        AudioDeviceInfo[] audioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
        for (AudioDeviceInfo deviceInfo : audioDevices) {
            list.add(getInfo(deviceInfo));
        }
        android.util.Log.d(TAG, "list" + list);
        return list;
    }

    @Override
    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
        // Unregister receiver
        if (audioChangeReceiver != null && context != null) {
            try {
                context.unregisterReceiver(audioChangeReceiver);
            } catch (IllegalArgumentException e) {
                Log.w(TAG, "Receiver not registered: " + e.getMessage());
            }
        }

        // Clean up channel
        if (channel != null) {
            channel.setMethodCallHandler(null);
            channel = null;
        }

        // Reset audio manager to normal mode
        if (audioManager != null) {
            audioManager.setMode(AudioManager.MODE_NORMAL);
        }

        context = null;
        audioManager = null;
        audioChangeReceiver = null;
    }

    private String typeToString(int type) {
        String typeName = "unknown";
        switch (type) {
            case AudioDeviceInfo.TYPE_UNKNOWN:
                typeName = "unknown";
                break;
            case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
                typeName = "built-in earpiece";
                break;
            case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
                typeName = "built-in speaker";
                break;
            case AudioDeviceInfo.TYPE_WIRED_HEADSET:
                typeName = "wired headset";
                break;
            case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
                typeName = "wired headphones";
                break;
            case AudioDeviceInfo.TYPE_LINE_ANALOG:
                typeName = "line analog";
                break;
            case AudioDeviceInfo.TYPE_LINE_DIGITAL:
                typeName = "line digital";
                break;
            case AudioDeviceInfo.TYPE_BLUETOOTH_SCO:
                typeName = "Bluetooth telephony SCO";
                break;
            case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
                typeName = "Bluetooth A2DP";
                break;
            case AudioDeviceInfo.TYPE_HDMI:
                typeName = "HDMI";
                break;
            case AudioDeviceInfo.TYPE_HDMI_ARC:
                typeName = "HDMI audio return channel";
                break;
            case AudioDeviceInfo.TYPE_USB_DEVICE:
                typeName = "USB device";
                break;
            case AudioDeviceInfo.TYPE_USB_ACCESSORY:
                typeName = "USB accessory";
                break;
            case AudioDeviceInfo.TYPE_DOCK:
                typeName = "dock";
                break;
            case AudioDeviceInfo.TYPE_FM:
                typeName = "FM";
                break;
            case AudioDeviceInfo.TYPE_BUILTIN_MIC:
                typeName = "built-in microphone";
                break;
            case AudioDeviceInfo.TYPE_FM_TUNER:
                typeName = "FM tuner";
                break;
            case AudioDeviceInfo.TYPE_TV_TUNER:
                typeName = "TV tuner";
                break;
            case AudioDeviceInfo.TYPE_TELEPHONY:
                typeName = "telephony";
                break;
            case AudioDeviceInfo.TYPE_AUX_LINE:
                typeName = "auxiliary line-level connectors";
                break;
            case AudioDeviceInfo.TYPE_IP:
                typeName = "IP";
                break;
            case AudioDeviceInfo.TYPE_BUS:
                typeName = "bus";
                break;
            case AudioDeviceInfo.TYPE_USB_HEADSET:
                typeName = "USB headset";
                break;
            case AudioDeviceInfo.TYPE_HEARING_AID:
                typeName = "hearing aid";
                break;
            case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER_SAFE:
                typeName = "built-in speaker safe";
                break;
            case AudioDeviceInfo.TYPE_REMOTE_SUBMIX: // 25
                typeName = "remote submix";
                break;
            case AudioDeviceInfo.TYPE_BLE_HEADSET:
                typeName = "BLE headset";
                break;
            case AudioDeviceInfo.TYPE_BLE_SPEAKER:
                typeName = "BLE speaker";
                break;
            case 28: // AudioDeviceInfo.TYPE_ECHO_REFERENCE
                typeName = "echo reference";
                break;
            case AudioDeviceInfo.TYPE_HDMI_EARC:
                typeName = "HDMI enhanced ARC";
                break;
            case AudioDeviceInfo.TYPE_BLE_BROADCAST:
                typeName = "BLE broadcast";
                break;
            default:
                typeName = "unknown=" + type;
                break;
        }
        return typeName;
    }
}
